package {
    import away3d.cameras.SpringCam;
    import away3d.containers.ObjectContainer3D;
    import away3d.containers.View3D;
    import away3d.core.math.MathConsts;
    import away3d.debug.AwayStats;
    import away3d.entities.Mesh;
    import away3d.events.LoaderEvent;
    import away3d.lights.DirectionalLight;
    import away3d.lights.LightBase;
    import away3d.loaders.Loader3D;
    import away3d.loaders.parsers.OBJParser;
    import away3d.materials.BitmapMaterial;
    import away3d.materials.utils.CubeMap;
    import away3d.primitives.SkyBox;
    import com.bit101.components.HUISlider;
    import flash.display.*;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.*;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Vector3D;
    import flash.net.URLRequest;
    import flash.ui.Keyboard;
    import jiglib.geometry.JTerrain;
    import jiglib.math.*;
    import jiglib.physics.*;
    import jiglib.plugin.away3d4.*;
    import jiglib.vehicles.JCar;
	
	
    // import away3d.events.AssetEvent;
	/**
	 * TODO
   * - Generated texture bitmap could be much more complex/detailed/rich. 
   *   But it's not worth, since exists terragen-like tools that allows you to do exactly that.
   * - Level of detail heightmap chuncks. The engine should provide some facilities to do that, although.
	 * @author yo
	 */
	public class CarDemo extends Sprite {
    
    [Embed(source="../embeds/height_map.png")]
    private var rHeightMap : Class;
    
    [Embed(source="../embeds/fskin.jpg")]
    private var CarSkin : Class;
        
    // Environment map.
    [Embed(source="../embeds/envMap/mars_positive_x.jpg")]
    private var EnvPosX:Class;
    [Embed(source="../embeds/envMap/mars_positive_y.jpg")]
    private var EnvPosY:Class;
    [Embed(source="../embeds/envMap/mars_positive_z.jpg")]
    private var EnvPosZ:Class;
    [Embed(source="../embeds/envMap/mars_negative_x.jpg")]
    private var EnvNegX:Class;
    [Embed(source="../embeds/envMap/mars_negative_y.jpg")]
    private var EnvNegY:Class;
    [Embed(source="../embeds/envMap/mars_negative_z.jpg")]
    private var EnvNegZ:Class;

    [Embed(source="../embeds/sand_128.jpg")]
    private var rSand : Class;


    private const ANTIALIAS:int = 4;


//    private var _camController:HoverDragController;
    private var _view:View3D;
    private var _light:LightBase;
    private var _cubeMap:CubeMap;

    private var container:ObjectContainer3D;
    private var steerFR:ObjectContainer3D;
    private var steerFL:ObjectContainer3D;
    private var wheelFR:Mesh;
    private var wheelFL:Mesh;
    private var wheelBR:Mesh;
    private var wheelBL:Mesh;

    private var carBody:JCar;
    private var physics:Away3D4Physics;
    private var boxBody:Vector.<RigidBody>;
    private var terrain:JTerrain;
		
		public function CarDemo():void {
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event = null):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
            
      // Init stage.
      stage.scaleMode = StageScaleMode.NO_SCALE;
      stage.align = StageAlign.TOP_LEFT;
      stage.frameRate = 60;

      // Init 3D core.
      _view = new View3D();
      addChild(_view);
      // _view.camera = new SpringCam();
      
      //_camController = new HoverDragController(_view.camera, stage);
      // _camController.radius = 5000;
      
      // Camera
      _view.camera.lens.far = 150000;

      // Show Away3D stats.
      var stats:AwayStats = new AwayStats(_view);
      stats.x = stage.stageWidth - stats.width;
      addChild(stats);
      
      // Lights.
      var ld:Vector3D = new Vector3D(-0.5, -1, -0.5);
      ld.scaleBy(1);
      _light = new DirectionalLight(ld.x, ld.y, ld.z); // DirectionalLight();
      _light.color = 0xFFFFFF;
      _light.specular = 1;
      _light.diffuse = 1;
      _light.castsShadows = true;
      _view.scene.addChild(_light);
      
      // SkyBox
      _cubeMap = new CubeMap(new EnvPosX().bitmapData, new EnvNegX().bitmapData,
          new EnvPosY().bitmapData, new EnvNegY().bitmapData,
          new EnvPosZ().bitmapData, new EnvNegZ().bitmapData);
          
      _view.scene.addChild(new SkyBox(_cubeMap));
      
      // var hm:ComplexHeightMap = new ComplexHeightMap(_view, null/* _camController*/);
      
      _view.antiAlias = ANTIALIAS;
      
      // Start loop
      addEventListener(Event.ENTER_FRAME, enterFrameHandler);
      stage.addEventListener( KeyboardEvent.KEY_DOWN, keyDownHandler );
			stage.addEventListener( KeyboardEvent.KEY_UP, keyUpHandler );
      
      
      physics = new Away3D4Physics(_view, 6);
      
      var material:BitmapMaterial = new BitmapMaterial((new rSand() as Bitmap).bitmapData);
      // var ground:RigidBody = physics.createGround(material);
      
			terrain = physics.createTerrain(material, (new rHeightMap() as Bitmap).bitmapData, 10000, 600, 10000, 64, 64, 300, 0, false);
      
      var _loader:Loader3D = new Loader3D();
			_loader.load(new URLRequest('../embeds/car.obj'), new OBJParser());
			_loader.addEventListener(LoaderEvent.RESOURCE_COMPLETE, onResourceComplete);
      
      var camXSlider:HUISlider = new HUISlider(this, 0, 0, "Camera X", onCamX);
      camXSlider.minimum = -5000;
      camXSlider.maximum = 5000;
      
      var camYSlider:HUISlider = new HUISlider(this, 0, 20, "Camera Y", onCamY);
      camYSlider.minimum = -5000;
      camYSlider.maximum = 5000;
      
      var camZSlider:HUISlider = new HUISlider(this, 0, 40, "Camera >", onCamZ);      
      camZSlider.minimum = -5000;
      camZSlider.maximum = 5000;
		}
    private var cameraPos:Vector3D = new Vector3D(3000, 1300, 0);
    private function onCamX(e:Event):void { cameraPos.x = (e.target as HUISlider).value; }
    private function onCamY(e:Event):void { cameraPos.y = (e.target as HUISlider).value; }
    private function onCamZ(e:Event):void { cameraPos.z = (e.target as HUISlider).value; }
    
    private function onResourceComplete(event : LoaderEvent) : void
		{
			container=ObjectContainer3D(event.target);
			_view.scene.addChild(container);
			
			var carMaterial:BitmapMaterial = new BitmapMaterial(new CarSkin().bitmapData);
			// TODO carMaterial.lights = [mylight];
			
			var mesh : Mesh;
			for (var i : int = 0; i < container.numChildren; ++i) {
                mesh = Mesh(container.getChildAt(i));
                mesh.geometry.scale(40);
                mesh.material = carMaterial
            }
			
			carBody = new JCar(null);
			carBody.setCar(40, 1, 500);
			carBody.chassis.moveTo(new Vector3D(-200, 200, 0));
			carBody.chassis.rotationY = 90;
			carBody.chassis.mass = 10;
			carBody.chassis.sideLengths = new Vector3D(105, 40, 220);
			physics.addBody(carBody.chassis);
			
			carBody.setupWheel("WheelFL", new Vector3D(-48, -20, 68), 1.3, 1.3, 6, 20, 0.5, 0.5, 2);
			carBody.setupWheel("WheelFR", new Vector3D(48, -20, 68), 1.3, 1.3, 6, 20, 0.5, 0.5, 2);
			carBody.setupWheel("WheelBL", new Vector3D(-48, -20, -84), 1.3, 1.3, 6, 20, 0.5, 0.5, 2);
			carBody.setupWheel("WheelBR", new Vector3D(48, -20, -84), 1.3, 1.3, 6, 20, 0.5, 0.5, 2);
			
			wheelFL = Mesh(container.getChildAt(0));
			wheelFR = Mesh(container.getChildAt(3));
			wheelBL = Mesh(container.getChildAt(4));
			wheelBL.position = new Vector3D( -48, -20, -84);
			wheelBR = Mesh(container.getChildAt(2));
			wheelBR.position = new Vector3D(48, -20, -84);
			
			steerFL = new ObjectContainer3D();
			steerFL.position = new Vector3D(-48,-20,68);
			steerFL.addChild(wheelFL);
			container.addChild(steerFL);
			
			steerFR = new ObjectContainer3D();
			steerFR.position = new Vector3D(48,-20,68);
			steerFR.addChild(wheelFR);
			container.addChild(steerFR);
      
      // Setup camera (for spring cam).
      // setupCameraThirdPerson();
      // (_view.camera as SpringCam).target = container;
		}
    
    private function setupCameraFirstPerson():void
    {
      (_view.camera as SpringCam).target = container;
      (_view.camera as SpringCam).positionOffset = new Vector3D(0, 20, -0);
      (_view.camera as SpringCam).lookOffset = new Vector3D(0, 10, 100);
      //(_view.camera as SpringCam).stiffness = 0.05;
      (_view.camera as SpringCam).stiffness = 1;
    }
    
    private function setupCameraThirdPerson():void
    {
      (_view.camera as SpringCam).target = container;
      (_view.camera as SpringCam).positionOffset = new Vector3D(0, 200, -400);
      (_view.camera as SpringCam).lookOffset = new Vector3D(0, 10, 100);
      (_view.camera as SpringCam).stiffness = 0.05;
    }
    
    private function keyDownHandler(event :KeyboardEvent):void
		{
			switch(event.keyCode)
			{
				case Keyboard.UP:
					carBody.setAccelerate(1);
					break;
				case Keyboard.DOWN:
					carBody.setAccelerate(-1);
					break;
				case Keyboard.LEFT:
					carBody.setSteer(["WheelFL", "WheelFR"], -1);
					break;
				case Keyboard.RIGHT:
					carBody.setSteer(["WheelFL", "WheelFR"], 1);
					break;
				case Keyboard.SPACE:
					carBody.setHBrake(1);
					break;
			}
		}
		
		private function keyUpHandler(event:KeyboardEvent):void
		{
			switch(event.keyCode)
			{
				case Keyboard.UP:
					carBody.setAccelerate(0);
					break;
					
				case Keyboard.DOWN:
					carBody.setAccelerate(0);
					break;
					
				case Keyboard.LEFT:
					carBody.setSteer(["WheelFL", "WheelFR"], 0);
					break;
					
				case Keyboard.RIGHT:
					carBody.setSteer(["WheelFL", "WheelFR"], 0);
					break;
				case Keyboard.SPACE:
				   carBody.setHBrake(0);
			}
		}
		
		private function updateCarSkin():void
		{
			if (!carBody)
				return;
				
			container.transform = JMatrix3D.getAppendMatrix3D(carBody.chassis.currentState.orientation, JMatrix3D.getTranslationMatrix(carBody.chassis.currentState.position.x, carBody.chassis.currentState.position.y, carBody.chassis.currentState.position.z));
			
			wheelFL.pitch(carBody.wheels["WheelFL"].getRollAngle());
			wheelFR.pitch(carBody.wheels["WheelFR"].getRollAngle());
			wheelBL.pitch(carBody.wheels["WheelBL"].getRollAngle());
			wheelBR.pitch(carBody.wheels["WheelBR"].getRollAngle());
			
			steerFL.rotationY = carBody.wheels["WheelFL"].getSteerAngle();
			steerFR.rotationY = carBody.wheels["WheelFR"].getSteerAngle();
			
			steerFL.y = carBody.wheels["WheelFL"].getActualPos().y;
			steerFR.y = carBody.wheels["WheelFR"].getActualPos().y;
			wheelBL.y = carBody.wheels["WheelBL"].getActualPos().y;
			wheelBR.y = carBody.wheels["WheelBR"].getActualPos().y;
			
			// _view.camera.position=container.position.add(new Vector3D(0,500,-500));
			// _view.camera.lookAt(container.position);
      // _camController.target = container.position;
		}
    
    private var previousPos:Vector3D = new Vector3D();
    private function updateCamera():void
    {
      if (!carBody) return;
      /*
      var currentPos:Vector3D = carBody.chassis.currentState.position.clone();
      
      var currentDir:Vector3D = previousPos.clone();
      currentDir.incrementBy(currentPos);
      currentDir.normalize();
      currentDir.scaleBy(1000);
      
      var camPos:Vector3D = currentPos.clone();
      camPos.decrementBy(currentDir);
      camPos.y = 10;
      
      _view.camera.position = camPos;
      _view.camera.lookAt(container.position);
      
      
      previousPos = currentPos;
      */
      
      /*
      var camPos:Vector3D = container.position;
      // trace(container.rotationY);
      var rotRad:Number = container.transform.decompose()[1].x * MathConsts.DEGREES_TO_RADIANS;
      // trace(rotRad);
      var dir:Vector3D = new Vector3D(Math.sin(-rotRad), 0, Math.cos(-rotRad));
      // dir.normalize();
      // trace(dir);
      dir.scaleBy(500);
      camPos.incrementBy(dir);
      
      //trace(container.rotationY);
      _view.camera.position = camPos;
      _view.camera.lookAt(container.position);*/
      
      _view.camera.position = cameraPos;
      _view.camera.lookAt(container.position)
    }            
    
    private function mouseDown(e:MouseEvent ):void
    {
    }
    
    private function enterFrameHandler(event:Event):void
    {
      _view.render();
      updateCarSkin();
      updateCamera();
      physics.step(0.1);
    }

	}
	
}

